/**
 * 位掩码
 *   为了操作各个位（例如打开或关闭它们），我们需要某种方法来识别我们想要操作的特定位。不幸的是，按位运算符不知道如何处理位位置。相反，他们使用位掩码。
 *   位掩码是一组预定义的位，用于选择后续操作将修改哪些特定位。
 * 
 * 由于 C++14 支持二进制文字，因此定义这些位掩码很容易
 * 
 * 
 * 位掩码和 std::bitset
 *   std::bitset 支持全套位运算符。因此，尽管使用函数（测试、设置、重置和翻转）来修改各个位更容易，但如果需要，也可以使用按位运算符和位掩码。
 * 
 * 位标志什么时候最有用？
 *   1.下面的例子实际上并没有节省任何内存。8 个独立的布尔值通常占用 8 个字节。但上面的示例（使用 std::uint8_t）使用 9 个字节——8 个字节来定义位掩码，1 个字节用于标志变量！
 *     有许多相同的标志变量时，位标志最有意义。例如，在上面的示例中，假设您有 100 个人（我），而不是一个人。
 *     如果每人使用 8 个布尔值（每个可能的状态一个），那么将使用 800 字节的内存。对于位标志，将使用 8 个字节作为位掩码，使用 100 个字节作为位标志变量，总共 108 个字节的内存——大约减少了 8 倍的内存。
 *   2.想象一下，有一个函数可以采用 32 个不同选项的任意组合。编写该函数的一种方法是使用 32 个单独的布尔参数。
 *     这非常难以阅读和使用，如果使用位标志定义函数可以简单许多
 * 
 * 这更具可读性。
 *   这是 OpenGL（一个备受推崇的 3D 图形库）选择使用位标志参数而不是许多连续布尔参数的原因之一。
 * 
 *   下面是来自 OpenGL 的函数调用示例：
 *   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color and the depth buffer
 * 
 *   GL_COLOR_BUFFER_BIT 和 GL_DEPTH_BUFFER_BIT 是位掩码，定义如下（在 gl2.h 中）：
 *   #define GL_DEPTH_BUFFER_BIT               0x00000100
 *   #define GL_STENCIL_BUFFER_BIT             0x00000400
 *   #define GL_COLOR_BUFFER_BIT               0x00004000
 * 
 * 
 * 概括
 * 
 * 总结如何设置、清除、切换和查询位标志：
 * 
 * 要查询位状态，我们使用按位 AND：
 * if (flags & option4) ... // if option4 is set, do something
 * 
 * 要设置位（打开），我们使用按位 OR：
 * flags |= option4; // turn option 4 on.
 * flags |= (option4 | option5); // turn options 4 and 5 on.
 * 
 * 要清除位（关闭），我们使用按位 AND和按位 NOT：
 * flags &= ~option4; // turn option 4 off
 * flags &= ~(option4 | option5); // turn options 4 and 5 off
 * 
 * 为了翻转位状态，我们使用按位 XOR：
 * flags ^= option4; // flip option4 from on to off, or vice versa
 * flags ^= (option4 | option5); // flip options 4 and 5
 * 
 */

#include <iostream>
#include <cstdint> // for uint8_t
#include <bitset>

using namespace std;

int main() {
    constexpr uint8_t mask0{ 0b0000'0001 }; // represents bit 0
    constexpr uint8_t mask1{ 0b0000'0010 }; // represents bit 1
    constexpr uint8_t mask2{ 0b0000'0100 }; // represents bit 2
    constexpr uint8_t mask3{ 0b0000'1000 }; // represents bit 3
    constexpr uint8_t mask4{ 0b0001'0000 }; // represents bit 4
    constexpr uint8_t mask5{ 0b0010'0000 }; // represents bit 5
    constexpr uint8_t mask6{ 0b0100'0000 }; // represents bit 6
    constexpr uint8_t mask7{ 0b1000'0000 }; // represents bit 7

    // c++11 或更早 使用十六进制来表示
    // constexpr uint8_t mask0{ 0x01 }; // hex for 0000 0001
    // constexpr uint8_t mask1{ 0x02 }; // hex for 0000 0010
    // constexpr uint8_t mask2{ 0x04 }; // hex for 0000 0100
    // constexpr uint8_t mask3{ 0x08 }; // hex for 0000 1000
    // constexpr uint8_t mask4{ 0x10 }; // hex for 0001 0000
    // constexpr uint8_t mask5{ 0x20 }; // hex for 0010 0000
    // constexpr uint8_t mask6{ 0x40 }; // hex for 0100 0000
    // constexpr uint8_t mask7{ 0x80 }; // hex for 1000 0000

    // 一种更简单的方法 使用左移运算符
    // constexpr uint8_t mask0{ 1 << 0 }; // 0000 0001
    // constexpr uint8_t mask1{ 1 << 1 }; // 0000 0010
    // constexpr uint8_t mask2{ 1 << 2 }; // 0000 0100
    // constexpr uint8_t mask3{ 1 << 3 }; // 0000 1000
    // constexpr uint8_t mask4{ 1 << 4 }; // 0001 0000
    // constexpr uint8_t mask5{ 1 << 5 }; // 0010 0000
    // constexpr uint8_t mask6{ 1 << 6 }; // 0100 0000
    // constexpr uint8_t mask7{ 1 << 7 }; // 1000 0000

    std::uint8_t flags{ 0b0000'0101 }; // 8 bits in size means room for 8 flags

    // 确定某个位是打开还是关闭，我们将按位与与相应位的位掩码结合使用
	std::cout << "bit 0 is " << (static_cast<bool>(flags & mask0) ? "on\n" : "off\n");
	std::cout << "bit 1 is " << (static_cast<bool>(flags & mask1) ? "on\n" : "off\n");

    // 要设置（打开）一个位（值为 1），我们使用按位或等于（运算符 |=）以及相应位的位掩码
    std::cout << "bit 1 is " << (static_cast<bool>(flags & mask1) ? "on\n" : "off\n");

    flags |= mask1; // turn on bit 1

    std::cout << "bit 1 is " << (static_cast<bool>(flags & mask1) ? "on\n" : "off\n");


    // 要重置（清除）一位（值为 0），我们一起使用按位与和按位非
    flags = 0b0000'0101; // 8 bits in size means room for 8 flags

    std::cout << "bit 2 is " << (static_cast<bool>(flags & mask2) ? "on\n" : "off\n");

    flags &= ~mask2; // turn off bit 2

    std::cout << "bit 2 is " << (static_cast<bool>(flags & mask2) ? "on\n" : "off\n");
    // 同时关闭多个位
    flags &= ~(mask4 | mask5); // turn bits 4 and 5 off at the same time


    // 要切换（翻转）位状态（从 0 到 1 或从 1 到 0），我们使用按位异或
    flags = 0b0000'0101; // 8 bits in size means room for 8 flags
    std::cout << "bit 2 is " << (static_cast<bool>(flags & mask2) ? "on\n" : "off\n");
    flags ^= mask2; // flip bit 2
    std::cout << "bit 2 is " << (static_cast<bool>(flags & mask2) ? "on\n" : "off\n");
    flags ^= mask2; // flip bit 2
    std::cout << "bit 2 is " << (static_cast<bool>(flags & mask2) ? "on\n" : "off\n");
    // 可以同时翻转多个位
    flags ^= (mask4 | mask5); // flip bits 4 and 5 at the same time



    // bitset
    std::bitset<8> flags1 = 0b0000'0101; // 8 bits in size means room for 8 flags1
	std::cout << "bit 1 is " << (flags1.test(1) ? "on\n" : "off\n");
	std::cout << "bit 2 is " << (flags1.test(2) ? "on\n" : "off\n");

	flags1 ^= (mask1 | mask2); // flip bits 1 and 2
	std::cout << "bit 1 is " << (flags1.test(1) ? "on\n" : "off\n");
	std::cout << "bit 2 is " << (flags1.test(2) ? "on\n" : "off\n");

	flags1 |= (mask1 | mask2); // turn bits 1 and 2 on
	std::cout << "bit 1 is " << (flags1.test(1) ? "on\n" : "off\n");
	std::cout << "bit 2 is " << (flags1.test(2) ? "on\n" : "off\n");

	flags1 &= ~(mask1 | mask2); // turn bits 1 and 2 off
	std::cout << "bit 1 is " << (flags1.test(1) ? "on\n" : "off\n");
	std::cout << "bit 2 is " << (flags1.test(2) ? "on\n" : "off\n");



    // 让位掩码更有意义
    // Define a bunch of physical/emotional states
	[[maybe_unused]] constexpr std::uint8_t isHungry   { 1 << 0 }; // 0000 0001
	[[maybe_unused]] constexpr std::uint8_t isSad      { 1 << 1 }; // 0000 0010
	[[maybe_unused]] constexpr std::uint8_t isMad      { 1 << 2 }; // 0000 0100
	[[maybe_unused]] constexpr std::uint8_t isHappy    { 1 << 3 }; // 0000 1000
	[[maybe_unused]] constexpr std::uint8_t isLaughing { 1 << 4 }; // 0001 0000
	[[maybe_unused]] constexpr std::uint8_t isAsleep   { 1 << 5 }; // 0010 0000
	[[maybe_unused]] constexpr std::uint8_t isDead     { 1 << 6 }; // 0100 0000
	[[maybe_unused]] constexpr std::uint8_t isCrying   { 1 << 7 }; // 1000 0000

	std::uint8_t me{}; // all flags/options turned off to start
	me |= (isHappy | isLaughing); // I am happy and laughing
	me &= ~isLaughing; // I am no longer laughing

	// Query a few states
	// (we'll use static_cast<bool> to interpret the results as a boolean value)
	std::cout << "I am happy? " << static_cast<bool>(me & isHappy) << '\n';
	std::cout << "I am laughing? " << static_cast<bool>(me & isLaughing) << '\n';



    // 涉及多个位的位掩码
    // R、G、B 和 A 通常存储为单个 32 位整数，每个分量使用 8 位
    constexpr std::uint32_t redBits{ 0xFF000000 };
	constexpr std::uint32_t greenBits{ 0x00FF0000 };
	constexpr std::uint32_t blueBits{ 0x0000FF00 };
	constexpr std::uint32_t alphaBits{ 0x000000FF };

	std::cout << "Enter a 32-bit RGBA color value in hexadecimal (e.g. FF7F3300): ";
	std::uint32_t pixel{};
	std::cin >> std::hex >> pixel; // std::hex allows us to read in a hex value

	// use Bitwise AND to isolate the pixels for our given color,
	// then right shift the value into the lower 8 bits
	std::uint8_t red{ static_cast<std::uint8_t>((pixel & redBits) >> 24) };
	std::uint8_t green{ static_cast<std::uint8_t>((pixel & greenBits) >> 16) };
	std::uint8_t blue{ static_cast<std::uint8_t>((pixel & blueBits) >> 8) };
	std::uint8_t alpha{ static_cast<std::uint8_t>(pixel & alphaBits) };

	std::cout << "Your color contains:\n";
	std::cout << std::hex; // print the following values in hex

        // reminder: std::uint8_t will likely print as a char
        // we static_cast to int to ensure it prints as an integer
	std::cout << static_cast<int>(red)   << " red\n";
	std::cout << static_cast<int>(green) << " green\n";
	std::cout << static_cast<int>(blue)  << " blue\n";
	std::cout << static_cast<int>(alpha) << " alpha\n";
    
    return 0;
}